<template>
  <div class="knowledge-graph-container">
    <el-card class="box-card">
      <div slot="header" class="clearfix">
        <span>知识图谱管理</span>
        <el-upload
          class="upload-demo"
          action=""
          :auto-upload="false"
          :show-file-list="false"
          :before-upload="handleExcelUpload"
        >
          <el-button size="small" type="primary">导入维修记录Excel</el-button>
        </el-upload>
        <el-button style="margin-left: 10px;" type="success" @click="buildGraph">图谱构建</el-button>
        <el-input
          v-model="searchKeyword"
          placeholder="搜索设备型号，如‘加工中心’"
          style="width: 240px; margin-left: 20px;"
          @input="handleSearch"
          clearable
        />
      </div>
      <div style="margin: 20px 0;">
        <el-alert
          title="可导入Excel或直接体验mock数据。点击节点可查看案例详情。"
          type="info"
          show-icon
        />
      </div>
      <div ref="graphContainer" style="width: 100%; height: 600px;"></div>
    </el-card>
    <el-dialog :visible.sync="dialogVisible" width="500px" title="案例详情">
      <el-descriptions :column="1" border>
        <el-descriptions-item label="设备型号">{{ selectedCase.deviceType }}</el-descriptions-item>
        <el-descriptions-item label="故障现象">{{ selectedCase.fault }}</el-descriptions-item>
        <el-descriptions-item label="处理过程">{{ selectedCase.process }}</el-descriptions-item>
        <el-descriptions-item label="备件信息">{{ selectedCase.sparePart }}</el-descriptions-item>
        <el-descriptions-item label="故障频率">{{ selectedCase.frequency }}</el-descriptions-item>
        <el-descriptions-item label="维护成本">{{ selectedCase.cost }}</el-descriptions-item>
      </el-descriptions>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">关闭</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import * as XLSX from 'xlsx';
import echarts from 'echarts';

const STORAGE_KEY = 'knowledge_graph_cases';

// 简单NLP抽取模拟
function extractInfo(record) {
  // 假设record为对象，含有描述字段
  // 真实NLP可用分词/实体识别，这里用关键词和正则模拟
  const faultMatch = record.desc.match(/(异响|漏油|过热|卡死|报警|损坏)/);
  const processMatch = record.desc.match(/(更换|修复|清洗|调整|加固|紧固|更注|更换皮带轮|更换轴承|更换电机)/);
  const spareMatch = record.desc.match(/(皮带型号[\w\d]+|轴承型号[\w\d]+|电机型号[\w\d]+)/);
  return {
    fault: faultMatch ? faultMatch[0] : '未知',
    process: processMatch ? processMatch[0] : '未知',
    sparePart: spareMatch ? spareMatch[0] : '未知',
  };
}

// 设备型号-故障频率-维护成本映射模拟
function mapToDimensions(record) {
  // 随机生成频率和成本
  return {
    frequency: Math.floor(Math.random() * 20 + 1) + '次/年',
    cost: Math.floor(Math.random() * 5000 + 500) + '元',
  };
}

// 生成mock数据
function generateMockCases(count = 100) {
  const deviceTypes = ['加工中心', '数控车床', '立式铣床', '卧式镗床'];
  const faults = ['主轴异响', '液压漏油', '电机过热', '刀库卡死', '系统报警'];
  const processes = ['更换皮带轮', '修复油封', '清洗冷却系统', '调整刀库', '更换电机'];
  const spares = ['皮带型号SPZ1250', '油封型号TC25', '电机型号Y180M', '轴承型号6205'];
  const cases = [];
  for (let i = 0; i < count; i++) {
    const deviceType = deviceTypes[Math.floor(Math.random() * deviceTypes.length)];
    const fault = faults[Math.floor(Math.random() * faults.length)];
    const process = processes[Math.floor(Math.random() * processes.length)];
    const sparePart = spares[Math.floor(Math.random() * spares.length)];
    const frequency = Math.floor(Math.random() * 20 + 1) + '次/年';
    const cost = Math.floor(Math.random() * 5000 + 500) + '元';
    cases.push({
      id: i + 1,
      deviceType,
      fault,
      process,
      sparePart,
      frequency,
      cost,
      desc: `${deviceType}发生${fault}，处理过程：${process}，使用${sparePart}`
    });
  }
  return cases;
}

export default {
  name: 'KnowledgeGraph',
  data() {
    return {
      cases: [], // 所有案例
      filteredCases: [], // 搜索后案例
      searchKeyword: '',
      dialogVisible: false,
      selectedCase: {},
      chart: null,
    };
  },
  mounted() {
    this.loadCases();
    this.$nextTick(() => {
      this.initChart();
    });
  },
  methods: {
    // Excel上传处理
    handleExcelUpload(file) {
      const reader = new FileReader();
      reader.onload = (e) => {
        const data = new Uint8Array(e.target.result);
        const workbook = XLSX.read(data, { type: 'array' });
        const sheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[sheetName];
        const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
        // 假设第一行为表头，后面为数据
        const header = jsonData[0];
        const records = jsonData.slice(1).map((row, idx) => {
          const record = {};
          header.forEach((key, i) => {
            record[key] = row[i] || '';
          });
          // NLP抽取
          const nlp = extractInfo(record);
          const dim = mapToDimensions(record);
          return {
            id: Date.now() + idx,
            deviceType: record['设备型号'] || '未知',
            fault: nlp.fault,
            process: nlp.process,
            sparePart: nlp.sparePart,
            frequency: dim.frequency,
            cost: dim.cost,
            desc: record['描述'] || ''
          };
        });
        this.cases = records;
        this.saveCases();
        this.handleSearch();
        this.$message.success('Excel导入成功，已自动抽取信息！');
        this.buildGraph();
      };
      reader.readAsArrayBuffer(file.raw);
      return false; // 阻止自动上传
    },
    // 保存到localStorage
    saveCases() {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(this.cases));
    },
    // 加载案例
    loadCases() {
      const data = localStorage.getItem(STORAGE_KEY);
      if (data) {
        this.cases = JSON.parse(data);
      } else {
        // 没有数据时生成mock
        this.cases = generateMockCases(200);
        this.saveCases();
      }
      this.filteredCases = this.cases;
    },
    // 搜索
    handleSearch() {
      if (!this.searchKeyword) {
        this.filteredCases = this.cases;
      } else {
        this.filteredCases = this.cases.filter(item =>
          item.deviceType.includes(this.searchKeyword)
        );
      }
      this.buildGraph();
    },
    // 构建关系图
    buildGraph() {
      if (!this.chart) {
        this.initChart();
      }
      // 只展示前1000条，防止卡顿
      const cases = this.filteredCases.slice(0, 1000);
      // 构建节点和边
      const nodes = [];
      const links = [];
      const nodeMap = {};
      cases.forEach((item, idx) => {
        // 设备型号节点
        if (!nodeMap[item.deviceType]) {
          nodes.push({
            id: 'device-' + item.deviceType,
            name: item.deviceType,
            category: 0,
            symbolSize: 60,
            value: item.deviceType,
            caseIds: []
          });
          nodeMap[item.deviceType] = true;
        }
        // 故障类型节点
        if (!nodeMap['fault-' + item.fault]) {
          nodes.push({
            id: 'fault-' + item.fault,
            name: item.fault,
            category: 1,
            symbolSize: 50,
            value: item.fault,
            caseIds: []
          });
          nodeMap['fault-' + item.fault] = true;
        }
        // 备件节点
        if (!nodeMap['spare-' + item.sparePart]) {
          nodes.push({
            id: 'spare-' + item.sparePart,
            name: item.sparePart,
            category: 2,
            symbolSize: 40,
            value: item.sparePart,
            caseIds: []
          });
          nodeMap['spare-' + item.sparePart] = true;
        }
        // 维护成本节点
        if (!nodeMap['cost-' + item.cost]) {
          nodes.push({
            id: 'cost-' + item.cost,
            name: item.cost,
            category: 3,
            symbolSize: 30,
            value: item.cost,
            caseIds: []
          });
          nodeMap['cost-' + item.cost] = true;
        }
        // 故障频率节点
        if (!nodeMap['freq-' + item.frequency]) {
          nodes.push({
            id: 'freq-' + item.frequency,
            name: item.frequency,
            category: 4,
            symbolSize: 30,
            value: item.frequency,
            caseIds: []
          });
          nodeMap['freq-' + item.frequency] = true;
        }
        // 设备-故障（实线）
        links.push({
          source: 'device-' + item.deviceType,
          target: 'fault-' + item.fault,
          lineStyle: { type: 'solid' },
          caseId: item.id
        });
        // 故障-备件（虚线）
        links.push({
          source: 'fault-' + item.fault,
          target: 'spare-' + item.sparePart,
          lineStyle: { type: 'dashed' },
          caseId: item.id
        });
        // 备件-维护成本（虚线）
        links.push({
          source: 'spare-' + item.sparePart,
          target: 'cost-' + item.cost,
          lineStyle: { type: 'dashed' },
          caseId: item.id
        });
        // 故障-频率（虚线）
        links.push({
          source: 'fault-' + item.fault,
          target: 'freq-' + item.frequency,
          lineStyle: { type: 'dashed' },
          caseId: item.id
        });
      });
      // 分类
      const categories = [
        { name: '设备型号' },
        { name: '故障类型' },
        { name: '备件' },
        { name: '维护成本' },
        { name: '故障频率' }
      ];
      const option = {
        tooltip: {
          formatter: params => {
            if (params.dataType === 'node') {
              return `<b>${params.data.name}</b><br/>类型：${categories[params.data.category].name}`;
            } else if (params.dataType === 'edge') {
              return '点击节点查看案例详情';
            }
            return '';
          }
        },
        legend: [{
          data: categories.map(a => a.name)
        }],
        series: [
          {
            type: 'graph',
            layout: 'force',
            symbolSize: 50,
            roam: true,
            label: {
              show: true
            },
            edgeSymbol: ['none', 'arrow'],
            edgeSymbolSize: [4, 10],
            edgeLabel: {
              fontSize: 12
            },
            data: nodes,
            links: links,
            categories: categories,
            force: {
              repulsion: 200,
              edgeLength: 120
            },
            lineStyle: {
              color: 'source',
              curveness: 0.2
            }
          }
        ]
      };
      this.chart.setOption(option);
      this.chart.off('click');
      this.chart.on('click', params => {
        if (params.dataType === 'node') {
          // 找到相关案例
          const node = params.data;
          let related = this.filteredCases.filter(item =>
            item.deviceType === node.value ||
            item.fault === node.value ||
            item.sparePart === node.value ||
            item.cost === node.value ||
            item.frequency === node.value
          );
          if (related.length > 0) {
            this.selectedCase = related[0];
            this.dialogVisible = true;
          }
        }
      });
    },
    // 初始化echarts
    initChart() {
      if (!this.chart) {
        this.chart = echarts.init(this.$refs.graphContainer);
      }
      this.buildGraph();
      window.addEventListener('resize', this.chart.resize);
    }
  },
  beforeDestroy() {
    if (this.chart) {
      window.removeEventListener('resize', this.chart.resize);
      this.chart.dispose();
      this.chart = null;
    }
  }
};
</script>

<style scoped>
.knowledge-graph-container {
  padding: 20px;
}
.box-card {
  margin: 0 auto;
  max-width: 1200px;
}
</style>
